﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace WpfExceptionWindow
{
    /// <summary>
    /// Interaction logic for InnerExceptionWindow.xaml
    /// </summary>
    partial class InnerExceptionWindow : Window
    {
        /// <summary>
        /// Gets the handle to this window.
        /// </summary>
        /// <value>The <see cref="System.IntPtr"/> handle.</value>
        private IntPtr Handle { get { return new System.Windows.Interop.WindowInteropHelper(this).Handle; } }


        /// <summary>
        /// Gets or sets the options.
        /// </summary>
        /// <value>The options.</value>
        public ExceptionWindowOptions Options
        {
            get { return (ExceptionWindowOptions)GetValue(OptionsProperty); }
            set { SetValue(OptionsProperty, value); }
        }

        /// <summary>
        /// Identifies the <see cref="WpfExceptionWindow.InnerExceptionWindow.Options"/> dependency property
        /// </summary>
        public static readonly DependencyProperty OptionsProperty =
            DependencyProperty.Register("Options", typeof(ExceptionWindowOptions), typeof(InnerExceptionWindow), new UIPropertyMetadata(ExceptionWindowOptions.None));

        /// <summary>
        /// Gets or sets the button.
        /// </summary>
        /// <value>The button.</value>
        public ExceptionWindowButton Button
        {
            get { return (ExceptionWindowButton)GetValue(ButtonProperty); }
            set { SetValue(ButtonProperty, value); }
        }

        /// <summary>
        /// Identifies the <see cref="WpfExceptionWindow.InnerExceptionWindow.Button"/> dependency property
        /// </summary>
        public static readonly DependencyProperty ButtonProperty =
            DependencyProperty.Register("Button", typeof(ExceptionWindowButton), typeof(InnerExceptionWindow), new UIPropertyMetadata(ExceptionWindowButton.Abort));

        /// <summary>
        /// Gets or sets the default result.
        /// </summary>
        /// <value>The default result.</value>
        public ExceptionWindowResult DefaultResult
        {
            get { return (ExceptionWindowResult)GetValue(DefaultResultProperty); }
            set { SetValue(DefaultResultProperty, value); }
        }

        /// <summary>
        /// Identifies the <see cref="WpfExceptionWindow.InnerExceptionWindow.DefaultResult"/> dependency property
        /// </summary>
        public static readonly DependencyProperty DefaultResultProperty =
            DependencyProperty.Register("DefaultResult", typeof(ExceptionWindowResult), typeof(InnerExceptionWindow), new UIPropertyMetadata(ExceptionWindowResult.None, DefaultResult_Changed, DefaultResult_Coerce));

        private static void DefaultResult_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) { }

        private static object DefaultResult_Coerce(DependencyObject sender, object value)
        {
            var defRes = (ExceptionWindowResult)value;
            var wndInner = sender as InnerExceptionWindow;
            if (wndInner != null)
                if (!wndInner.Button.IsPossibleResult(defRes))
                    return ExceptionWindowResult.Abort;

            return defRes;
        }

        /// <summary>
        /// Gets or sets the image.
        /// </summary>
        /// <value>The image.</value>
        public ExceptionWindowImage Image
        {
            get { return (ExceptionWindowImage)GetValue(ImageProperty); }
            set { SetValue(ImageProperty, value); }
        }
        /// <summary>
        /// Identifies the <see cref="WpfExceptionWindow.InnerExceptionWindow.Image"/> dependency property
        /// </summary>
        public static readonly DependencyProperty ImageProperty =
            DependencyProperty.Register("Image", typeof(ExceptionWindowImage), typeof(InnerExceptionWindow), new UIPropertyMetadata(ExceptionWindowImage.None));


        /// <summary>
        /// Gets or sets the result.
        /// </summary>
        /// <value>The result.</value>
        public ExceptionWindowResult Result { get; private set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="InnerExceptionWindow"/> class.
        /// </summary>
        /// <param name="owner">The window that will own the modal window.</param>
        /// <param name="exception">A <see cref="System.Exception"/> that specifies 
        /// the information to display.</param>
        /// <param name="caption">A <see cref="System.String"/> that specifies the 
        /// title bar caption to display.</param>
        /// <param name="button">A <see cref="ExceptionWindowResult"/> value that 
        /// specifies the button or buttons to display.</param>
        /// <param name="icon">A <see cref="ExceptionWindowImage"/> value that
        /// specifies the icon to display.</param>
        /// <param name="defaultResult">A <see cref="ExceptionWindowResult"/> value
        /// that specifies the default result of the exception window.</param>
        /// <param name="options">A <see cref="ExceptionWindowOptions"/> value object that
        /// specifies the options.</param>
        public InnerExceptionWindow(
            Window owner,
            Exception exception,
            string caption,
            ExceptionWindowButton button,
            ExceptionWindowImage icon,
            ExceptionWindowResult defaultResult,
            ExceptionWindowOptions options)
        {
            InitializeComponent();

            //Populate datacontexts with enums
            //I wish there was a way to do this in XAML
            btnAbort.DataContext = ExceptionWindowResult.Abort;
            btnContinue.DataContext = ExceptionWindowResult.Continue;
            btnRestart.DataContext = ExceptionWindowResult.Restart;
            btnRetry.DataContext = ExceptionWindowResult.Retry;

            this.Options = options;
            this.Button = button;
            this.DefaultResult = defaultResult;
            this.Image = icon;

            //Update the title
            this.Title = caption;

            //Set the windows datacontext
            this.DataContext = exception;

            //Update Image
            imgExceptionImage.DataContext = icon;

            //Updated buttons
            UpdateButtons(button);

            //Set the owner of the ExceptionWindow
            if (owner != null && owner.IsInitialized)
            {
                //Set the owner
                this.Owner = owner;
                //Set the icon
                this.Icon = this.Owner.Icon;
                //Set the startup location
                this.WindowStartupLocation = WindowStartupLocation.CenterOwner;
                //Set taskbar status
                this.ShowInTaskbar = true;
            }
            else
                this.WindowStartupLocation = WindowStartupLocation.CenterScreen;

            //Bind the last bit!
            this.SourceInitialized += new EventHandler(InnerExceptionWindow_SourceInitialized);
        }

        void InnerExceptionWindow_SourceInitialized(object sender, EventArgs e)
        {
            //Code design from: 
            //http://social.msdn.microsoft.com/forums/en-US/winforms/thread/e82b3622-ac23-4629-a47c-5b1136c673da

            //Get the window handle
            IntPtr windowHandle = new System.Windows.Interop.WindowInteropHelper(this).Handle;
            //Get menu handle
            IntPtr hMenu = NativeMethods.GetSystemMenu(windowHandle, false);

            if (hMenu != IntPtr.Zero)
            {
                int count = NativeMethods.GetMenuItemCount(hMenu);
                if (count > 0)
                {
                    //Remove close button
                    NativeMethods.RemoveMenu(hMenu, (uint)(count - 1), NativeMethods.MF_BYPOSITION | NativeMethods.MF_REMOVE);

                    //Redraw Menu
                    NativeMethods.DrawMenuBar(windowHandle);
                }
            }


            //Set the RTL options
            if (Options.IsSet(ExceptionWindowOptions.RtlReading))
            {
                int exStyle = NativeMethods.GetWindowLong(windowHandle, NativeMethods.GWL_EXSTYLE);
                int result = NativeMethods.SetWindowLong(windowHandle, NativeMethods.GWL_EXSTYLE, (int)(exStyle | NativeMethods.WS_EX_LAYOUTRTL));
#if DEBUG
                Console.WriteLine("SetWindowLong: " + result);
#endif

                NativeMethods.SetWindowPos(windowHandle, IntPtr.Zero, 0, 0, 0, 0,
                    NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOZORDER | NativeMethods.SWP_NOMOVE | NativeMethods.SWP_FRAMECHANGED);
            }

            //Seet Right Align options
            if (Options.IsSet(ExceptionWindowOptions.RightAlign))
            {
                txtExceptionMessage.TextAlignment = TextAlignment.Right;
            }
        }

        /// <summary>
        /// Updates the buttons based on the users options, removing them from the window.
        /// </summary>
        /// <param name="button">The <see cref="ExceptionWindowButton"/> value representing which buttons show on the window.</param>
        private void UpdateButtons(ExceptionWindowButton button)
        {

            switch (button)
            {
                case ExceptionWindowButton.Abort:
                    pnlButtonStack.Children.Remove(btnContinue);
                    pnlButtonStack.Children.Remove(btnRestart);
                    pnlButtonStack.Children.Remove(btnRetry);
                    break;
                case ExceptionWindowButton.AbortContinue:
                    pnlButtonStack.Children.Remove(btnRestart);
                    pnlButtonStack.Children.Remove(btnRetry);
                    break;
                case ExceptionWindowButton.AbortRetry:
                    pnlButtonStack.Children.Remove(btnContinue);
                    pnlButtonStack.Children.Remove(btnRestart);
                    break;
                case ExceptionWindowButton.AbortRestart:
                    pnlButtonStack.Children.Remove(btnContinue);
                    pnlButtonStack.Children.Remove(btnRetry);
                    break;
                case ExceptionWindowButton.AbortContinueRetry:
                    pnlButtonStack.Children.Remove(btnRestart);
                    break;
                case ExceptionWindowButton.AbortContinueRestart:
                    pnlButtonStack.Children.Remove(btnRetry);
                    break;
                default:
                    break;
            }
        }

        private void btnResult_Click(object sender, RoutedEventArgs e)
        {
            var btn = sender as Button;
            if (btn != null)
            {
                Result = (ExceptionWindowResult)btn.DataContext;
                this.Close();
            }
        }

        private void btnCopyToClipboard_Click(object sender, RoutedEventArgs e)
        {
            var exception = lvExceptionList.SelectedItem as Exception;
            if (exception != null)
                Clipboard.SetText(exception.ToString());
        }
    }
}
